package com.corx.network.mysqlstorage;

import com.corx.network.core.model.ReceivedMessage;
import com.corx.network.core.storage.AbstractStorage;
import com.corx.network.core.type.StorageTypeEnum;
import com.corx.network.mysqlstorage.hub.DeviceRepositoryHub;
import com.corx.network.mysqlstorage.model.Device;
import com.corx.network.mysqlstorage.model.DeviceDataRaw;
import com.corx.network.mysqlstorage.model.DeviceModelBind;
import com.corx.network.mysqlstorage.model.DeviceModelField;
import com.corx.network.mysqlstorage.util.TableNameGeneratorUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author gx
 * @version 1.0.0
 */
@Slf4j
public class DefaultMySqlStorage extends AbstractStorage {

    private final DeviceRepositoryHub repositoryHub;

    private final TableNameGeneratorUtil.TimeGranularity timeGranularity;

    public DefaultMySqlStorage(DeviceRepositoryHub repositoryHub,TableNameGeneratorUtil.TimeGranularity timeGranularity) {
        this.repositoryHub = Objects.requireNonNull(repositoryHub,"设备存储库中心，DeviceRepositoryHub == null");
        this.timeGranularity =  Objects.requireNonNull(timeGranularity,"时间粒度不能为空，TimeGranularity == null");
    }

    public DeviceRepositoryHub getRepositoryHub() {
        return repositoryHub;
    }

    public TableNameGeneratorUtil.TimeGranularity getTimeGranularity() {
        return timeGranularity;
    }

    @Override
    public StorageTypeEnum type() {
        return StorageTypeEnum.MYSQL;
    }


    @Override
    public void doSaveReceivedMessage(ReceivedMessage message) {

        String deviceId = getDeviceByDeviceId(message.getDeviceId()).map(
            device -> device.getId().toString()
        ).orElseGet(() -> null);

        if (deviceId == null) {
            log.warn("上报数据查询不到设备，return不做任何处理，ReceivedMessage:{}", message);
            return;
        }

        String rawJson = message.getTranslatedPayload();

        List<Long> modelIds = getModelIdByDeviceId(deviceId);

        for (Long modelId : modelIds) {

            // 1. 保存原始数据
            insertRawData(deviceId, modelId, rawJson);

            // 2. 获取字段模型
            List<DeviceModelField> fields = getFieldsByModelId(modelId);

            // 3. 保证结构化表存在
            ensureStructuredTable(generateStructuredTableName(deviceId), fields);

            // 4. 插入结构化数据
            insertStructuredData(generateStructuredTableName(deviceId), deviceId, modelId, fields, rawJson);
        }

    }

    public Optional<Device> getDeviceByDeviceId(String deviceId) {
        return repositoryHub.deviceRepo().findById(Long.parseLong(deviceId));
    }


    public List<Long> getModelIdByDeviceId(String deviceId) {
        List<DeviceModelBind> binds = repositoryHub.modelBindRepo().findByDeviceId(deviceId);
        return binds.stream().map(DeviceModelBind::getModelId).collect(Collectors.toList());
    }


    public List<DeviceModelField> getFieldsByModelId(Long modelId) {
        return repositoryHub.fieldRepo().findByModelId(modelId);
    }

    public void insertRawData(String deviceId, Long modelId, String rawJson) {
        DeviceDataRaw deviceDataRaw = new DeviceDataRaw();
        deviceDataRaw.setDeviceId(Long.parseLong(deviceId));
        deviceDataRaw.setModelId(modelId);
        deviceDataRaw.setRawJson(rawJson);
        repositoryHub.rawRepo().save(deviceDataRaw);
    }


    public void ensureStructuredTable(String tableName, List<DeviceModelField> fields) {
        repositoryHub.structuredRepo().ensureStructuredTable(tableName, fields);
    }


    public void insertStructuredData(String tableName, String deviceId, Long modelId, List<DeviceModelField> fields, String rawJson) {
        repositoryHub.structuredRepo().insertStructuredData(tableName, deviceId, modelId, fields, rawJson);
    }


    public String generateStructuredTableName(String deviceId) {
        return TableNameGeneratorUtil.generateStructuredTableName(deviceId,getTimeGranularity());
    }


}
